﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SocketLib.Buffers
{
	public class FastBuffer : IBuffer
	{
		#region basic data type sizes
		public static readonly int SIZE_SHORT = sizeof(short);
		public static readonly int SIZE_INT16 = sizeof(Int16);
		public static readonly int SIZE_INT32 = sizeof(Int32);
		public static readonly int SIZE_INT64 = sizeof(Int64);
		public static readonly int SIZE_LONG = sizeof(long);
		public static readonly int SIZE_FLOAT = sizeof(float);
		public static readonly int SIZE_DOUBLE = sizeof(double);
		#endregion

		#region
		private byte[] _data;
		private int _position;
		private int _limit;
		private int _capacity;
		private Boolean isAutoExtend = false;
		#endregion

		#region static create methods
		public static FastBuffer Allocate(int size)
		{
			var buffer = new FastBuffer();
			buffer._data = Array.CreateInstance(typeof(Byte), size) as byte[];
			buffer.Capacity = size;
			return buffer;
		}
		public static FastBuffer Wrap(byte[] data)
		{
			var buffer = new FastBuffer();
			buffer._data = data;
			buffer.Capacity = data.Length;
			return buffer;
		}
		#endregion
		#region IBuffer Members
		public long Remaining
		{
			get
			{
				return _limit - _position;
			}
		}
		public int Position
		{
			get
			{
				return _position;
			}
			set
			{
				if (value > _capacity)
					throw new BufferOverflowException();
				_position = value;
				if (_position > Limit)
				{
					Limit = _position;
				}
			}
		}

		public int Limit
		{
			get
			{
				return _limit;
			}
			set
			{
				if (value > _capacity)
					throw new BufferOverflowException();
				if (value < _position)
					_position = value;
				_limit = value;
			}
		}

		public int Capacity
		{
			get
			{
				return _capacity;
			}
			set
			{
				if (value < _limit)
					_limit = value;
				if (value < _position)
					_position = value;
				_capacity = value;
			}
		}

		public void Flip()
		{
			Limit = Position;
			Position = 0;
		}
		public void Rewind()
		{
			Position = 0;
		}
		public void Clear()
		{
			Position = 0;
			Limit = Capacity;
		}
		public void Reset()
		{
			Position = 0;
			Limit = 0;
		}
		public void Free()
		{
			_data = null;
			Position = 0;
			Limit = 0;
			Capacity = 0;
		}

		public byte[] toAllBytes()
		{
			return _data;
		}
		public byte[] copyAvaliableBytes()
		{
			var ret = new byte[_limit];
			Array.Copy(_data, 0, ret, 0, _limit);
			return ret;
		}
		#endregion

		#region IDataReader Members
		public byte ReadByte()
		{
			return Buffer.GetByte(_data, Position++);
		}

		public byte[] ReadBytes(int n)
		{
			byte[] ret = new byte[n];
			for (int i = 0; i < n; i++)
			{
				ret[i] = Buffer.GetByte(_data, Position++);
			}
			return ret;
		}
		public short ReadShort()
		{
			var ret = BitConverter.ToInt16(_data, Position);
			Position += SIZE_SHORT;
			return ret;
		}

		public short ReadInt16()
		{
			var ret = BitConverter.ToInt16(_data, Position);
			Position += SIZE_INT16;
			return ret;
		}

		public int ReadInt32()
		{
			var ret = BitConverter.ToInt32(_data, Position);
			Position += SIZE_INT32;
			return ret;
		}

		public long ReadInt64()
		{
			var ret = BitConverter.ToInt64(_data, Position);
			Position += SIZE_INT64;
			return ret;
		}

		public long ReadLong()
		{
			var ret = BitConverter.ToInt64(_data, Position);
			Position += SIZE_LONG;
			return ret;
		}

		public float ReadFloat()
		{
			var ret = BitConverter.ToSingle(_data, Position);
			Position += SIZE_FLOAT;
			return ret;
		}

		public double ReadDouble()
		{
			var ret = BitConverter.ToDouble(_data, Position);
			Position += SIZE_DOUBLE;
			return ret;
		}

		#endregion

		#region IDataWriter Members
		public void WriteByte(byte b)
		{
			if (_position + 1 > this.Capacity)
			{
				if (isAutoExtend)
				{
					var dest = new byte[_capacity + _capacity / 2 + 1];
					Buffer.BlockCopy(_data, 0, dest, 0, _data.Length);
				}
				else
					throw new BufferOverflowException();
			}
			Buffer.SetByte(_data, Position++, b);
		}
		public void WriteBytes(byte[] data, int start, int length)
		{
			if (_position + length > this.Capacity)
			{
				if (isAutoExtend)
				{
					_capacity = _capacity + _capacity / 2 + length;
					var dest = new byte[_capacity];
					Buffer.BlockCopy(_data, 0, dest, 0, _data.Length);
					_data = dest;
				}
				else
					throw new BufferOverflowException();
			}
			for (int i = start; i < start + length; i++)
			{
				Buffer.SetByte(_data, Position++, data[i]);
			}
		}
		public void WriteBytes(byte[] data)
		{
			WriteBytes(data, 0, data.Length);
		}
		public void WriteShort(short data)
		{
			WriteBytes(BitConverter.GetBytes(data));
		}

		public void WriteInt16(short data)
		{
			WriteBytes(BitConverter.GetBytes(data));
		}

		public void WriteInt32(int data)
		{
			WriteBytes(BitConverter.GetBytes(data));
		}

		public void WriteInt64(long data)
		{
			WriteBytes(BitConverter.GetBytes(data));
		}

		public void WriteLong(long data)
		{
			WriteBytes(BitConverter.GetBytes(data));
		}

		public void WriteFloat(float data)
		{
			WriteBytes(BitConverter.GetBytes(data));
		}

		public void WriteDouble(double data)
		{
			WriteBytes(BitConverter.GetBytes(data));
		}

		public void Append(byte[] data, int start, int length)
		{
			if (_position + length > this._capacity)
			{
				_capacity = _capacity + _capacity / 2 + data.Length;
				var dest = new byte[_capacity];
				Buffer.BlockCopy(_data, 0, dest, 0, _data.Length);
				_data = dest;
			}
			WriteBytes(data, start, length);
		}
		public bool AutoExtend
		{
			get
			{
				return isAutoExtend;
			}
			set
			{
				isAutoExtend = true;
			}
		}

		#endregion
	}

}
